import {
    walk,
    getNodeTreeBoundingRect,
    fullscrrenEvent,
    fullScreen,
    exitFullScreen,
    formatGetNodeGeneralization,
} from '../utils/index';
import { keyMap } from '../core/command/keyMap';

const defaultConfig = {
    boxShadowColor: 'rgba(0, 0, 0, 0.8)', // 高亮框四周的区域颜色
    borderRadius: '5px', // 高亮框的圆角大小
    transition: 'all 0.3s ease-out', // 高亮框动画的过渡
    zIndex: 9999, // 高亮框元素的层级
    padding: 20, // 高亮框的内边距
    margin: 50, // 高亮框的外边距
    openBlankMode: true, // 是否开启填空模式，即带下划线的文本默认不显示，按回车键才依次显示
};

// 演示插件
class Demonstrate {
    constructor(opt) {
        this.mindMap = opt.mindMap;
        // 是否正在演示中
        this.isInDemonstrate = false;
        // 演示的步骤列表
        this.stepList = [];
        // 当前所在步骤
        this.currentStepIndex = 0;
        // 当前所在步骤对应的节点实例
        this.currentStepNode = null;
        // 当前所在步骤节点的下划线文本数据
        this.currentUnderlineTextData = null;
        // 临时的样式剩余
        this.tmpStyleEl = null;
        // 高亮样式元素
        this.highlightEl = null;
        this.transformState = null;
        this.renderTree = null;
        this.config = Object.assign(
            { ...defaultConfig },
            this.mindMap.opt.demonstrateConfig || {},
        );
        this.needRestorePerformanceMode = false;
    }

    // 进入演示模式
    enter() {
        // 全屏
        this.bindFullscreenEvent();
        // 如果已经全屏了
        if (document.fullscreenElement === this.mindMap.el) {
            this._enter();
        } else {
            // 否则申请全屏
            fullScreen(this.mindMap.el);
        }
    }

    _enter() {
        this.isInDemonstrate = true;
        // 如果开启了性能模式，那么需要暂停
        this.pausePerformanceMode();
        // 添加演示用的临时的样式
        this.addTmpStyles();
        // 记录演示前的画布状态
        this.transformState = this.mindMap.view.getTransformData();
        // 记录演示前的画布数据
        this.renderTree = this.mindMap.getData();
        // 暂停收集历史记录
        this.mindMap.command.pause();
        // 暂停思维导图快捷键响应
        this.mindMap.keyCommand.pause();
        // 创建高亮元素
        this.createHighlightEl();
        // 计算步骤数据
        this.getStepList();
        // 收起所有节点
        let wait = false;
        if (this.mindMap.renderer.isRendering) {
            wait = true;
        }
        this.mindMap.execCommand('UNEXPAND_ALL', false);
        const onRenderEnd = () => {
            if (wait) {
                wait = false;
                return;
            }
            this.mindMap.off('node_tree_render_end', onRenderEnd);
            // 聚焦到第一步
            this.jump(this.currentStepIndex);
            this.bindEvent();
        };
        this.mindMap.on('node_tree_render_end', onRenderEnd);
    }

    // 退出演示模式
    exit() {
        exitFullScreen(this.mindMap.el);
        this.mindMap.updateData(this.renderTree);
        this.mindMap.view.setTransformData(this.transformState);
        this.renderTree = null;
        this.transformState = null;
        this.stepList = [];
        this.currentStepIndex = 0;
        this.currentStepNode = null;
        this.currentUnderlineTextData = null;
        this.unBindEvent();
        this.removeTmpStyles();
        this.removeHighlightEl();
        this.mindMap.command.recovery();
        this.mindMap.keyCommand.recovery();
        this.restorePerformanceMode();
        this.mindMap.emit('exit_demonstrate');
        this.isInDemonstrate = false;
    }

    // 暂停性能模式
    pausePerformanceMode() {
        const { openPerformance } = this.mindMap.opt;
        if (openPerformance) {
            this.needRestorePerformanceMode = true;
            this.mindMap.opt.openPerformance = false;
            this.mindMap.renderer.forceLoadNode();
        }
    }

    // 恢复性能模式
    restorePerformanceMode() {
        if (!this.needRestorePerformanceMode) return;
        this.mindMap.opt.openPerformance = true;
        this.mindMap.renderer.forceLoadNode();
    }

    // 添加临时的样式
    addTmpStyles() {
        this.tmpStyleEl = document.createElement('style');
        let cssText = `
      /* 画布所有元素禁止响应鼠标事件 */
      .smm-mind-map-container {
        pointer-events: none;
      }
      /* 超链接图标允许响应鼠标事件 */
      .smm-node a {
        pointer-events: all;
      }
      /* 备注图标允许响应鼠标事件 */
      .smm-node .smm-node-note {
        pointer-events: all;
      }
    `;
        if (this.config.openBlankMode) {
            cssText += `
        /* 带下划线的文本内容全部隐藏 */
        .smm-richtext-node-wrap u {
          opacity: 0;
        }
      `;
        }
        this.tmpStyleEl.innerText = cssText;
        document.head.appendChild(this.tmpStyleEl);
    }

    // 移除临时的样式
    removeTmpStyles() {
        if (this.tmpStyleEl) document.head.removeChild(this.tmpStyleEl);
    }

    // 创建高亮元素
    createHighlightEl() {
        if (!this.highlightEl) {
            // 高亮元素
            this.highlightEl = document.createElement('div');
            this.highlightEl.style.cssText = `
            position: absolute;
            box-shadow: 0 0 0 5000px ${this.config.boxShadowColor};
            border-radius: ${this.config.borderRadius};
            transition: ${this.config.transition};
            z-index: ${this.config.zIndex + 1};
            pointer-events: none;
        `;
            this.mindMap.el.appendChild(this.highlightEl);
        }
    }

    // 移除高亮元素
    removeHighlightEl() {
        if (this.highlightEl) {
            this.mindMap.el.removeChild(this.highlightEl);
            this.highlightEl = null;
        }
    }

    // 更新高亮元素的位置和大小
    updateHighlightEl({ left, top, width, height }) {
        const padding = this.config.padding;
        if (left) {
            this.highlightEl.style.left = left - padding + 'px';
        }
        if (top) {
            this.highlightEl.style.top = top - padding + 'px';
        }
        if (width) {
            this.highlightEl.style.width = width + padding * 2 + 'px';
        }
        if (height) {
            this.highlightEl.style.height = height + padding * 2 + 'px';
        }
    }

    // 绑定事件
    bindEvent() {
        this.onKeydown = this.onKeydown.bind(this);
        window.addEventListener('keydown', this.onKeydown);
    }

    // 绑定全屏事件
    bindFullscreenEvent() {
        this.onFullscreenChange = this.onFullscreenChange.bind(this);
        document.addEventListener(fullscrrenEvent, this.onFullscreenChange);
    }

    // 解绑事件
    unBindEvent() {
        window.removeEventListener('keydown', this.onKeydown);
        document.removeEventListener(fullscrrenEvent, this.onFullscreenChange);
    }

    // 全屏状态改变
    onFullscreenChange() {
        if (!document.fullscreenElement) {
            this.exit();
        } else if (document.fullscreenElement === this.mindMap.el) {
            this._enter();
        }
    }

    // 按键事件
    onKeydown(e) {
        // 上一个
        if (e.keyCode === keyMap.Left) {
            this.prev();
        } else if (e.keyCode === keyMap.Right) {
            // 下一个
            this.next();
        } else if (e.keyCode === keyMap.Esc) {
            // 退出演示
            this.exit();
        } else if (e.keyCode === keyMap.Enter) {
            // 回车键显示隐藏的下划线文本
            this.showNextUnderlineText();
        }
    }

    // 上一张
    prev() {
        if (this.currentStepIndex > 0) {
            this.jump(this.currentStepIndex - 1);
        }
    }

    // 下一张
    next() {
        const stepLength = this.stepList.length;
        if (this.currentStepIndex < stepLength - 1) {
            this.jump(this.currentStepIndex + 1);
        }
    }

    // 显示隐藏的下划线文本
    showNextUnderlineText() {
        if (
            !this.config.openBlankMode ||
            !this.currentStepNode ||
            !this.currentUnderlineTextData
        )
            return;
        const { index, list, length } = this.currentUnderlineTextData;
        if (index >= length) return;
        const node = list[index];
        this.currentUnderlineTextData.index++;
        node.node.style.opacity = 1;
    }

    // 跳转到某一张
    jump(index) {
        // 移除该当前下划线元素设置的样式
        if (this.currentUnderlineTextData) {
            this.currentUnderlineTextData.list.forEach((item) => {
                item.node.style.opacity = '';
            });
            this.currentUnderlineTextData = null;
        }
        this.currentStepNode = null;
        this.currentStepIndex = index;
        this.mindMap.emit(
            'demonstrate_jump',
            this.currentStepIndex,
            this.stepList.length,
        );
        const step = this.stepList[index];
        // 这一步的节点数据
        const nodeData = step.node;
        // 该节点的uid
        const uid = nodeData.data.uid;
        // 根据uid在画布上找到该节点实例
        const node = this.mindMap.renderer.findNodeByUid(uid);
        // 如果该节点实例不存在，那么先展开到该节点
        if (!node) {
            this.mindMap.renderer.expandToNodeUid(uid, () => {
                const node = this.mindMap.renderer.findNodeByUid(uid);
                // 展开后还是没找到，那么就别进入了，否则会死循环
                if (node) {
                    this.jump(index);
                }
            });
            return;
        }
        // 1.聚焦到某个节点
        if (step.type === 'node') {
            this.currentStepNode = node;
            // 当前节点存在带下划线的文本内容
            const uNodeList = this.config.openBlankMode
                ? node.group.find('u')
                : null;
            if (uNodeList && uNodeList.length > 0) {
                this.currentUnderlineTextData = {
                    index: 0,
                    list: uNodeList,
                    length: uNodeList.length,
                };
            }
            // 适应画布大小
            this.mindMap.view.fit(
                () => {
                    return node.group.rbox();
                },
                true,
                this.config.padding + this.config.margin,
            );
            const rect = node.group.rbox();
            this.updateHighlightEl({
                left: rect.x,
                top: rect.y,
                width: rect.width,
                height: rect.height,
            });
        } else {
            // 2.聚焦到某个节点的所有子节点
            // 聚焦该节点的所有子节点
            const task = () => {
                // 先收起该节点所有子节点的子节点
                nodeData.children.forEach((item) => {
                    item.data.expand = false;
                });
                this.mindMap.render(() => {
                    // 适应画布大小
                    this.mindMap.view.fit(
                        () => {
                            const res = getNodeTreeBoundingRect(
                                node,
                                0,
                                0,
                                0,
                                0,
                                true,
                            );
                            return {
                                ...res,
                                x: res.left,
                                y: res.top,
                            };
                        },
                        true,
                        this.config.padding + this.config.margin,
                    );
                    const res = getNodeTreeBoundingRect(node, 0, 0, 0, 0, true);
                    this.updateHighlightEl(res);
                });
            };
            // 如果该节点是收起状态，那么需要先展开
            if (!nodeData.data.expand) {
                this.mindMap.execCommand('SET_NODE_EXPAND', node, true);
                const onRenderEnd = () => {
                    this.mindMap.off('node_tree_render_end', onRenderEnd);
                    task();
                };
                this.mindMap.on('node_tree_render_end', onRenderEnd);
            } else {
                // 否则直接聚焦
                task();
            }
        }
    }

    // 深度度优先遍历所有节点，返回步骤列表
    getStepList() {
        walk(this.mindMap.renderer.renderTree, null, (node) => {
            this.stepList.push({
                type: 'node',
                node,
            });
            // 添加概要步骤
            const generalizationList = formatGetNodeGeneralization(node.data);
            generalizationList.forEach((item) => {
                // 没有uid的直接过滤掉，否则会死循环
                if (item.uid) {
                    this.stepList.push({
                        type: 'node',
                        node: {
                            data: item,
                        },
                    });
                }
            });
            if (node.children.length > 1) {
                this.stepList.push({
                    type: 'children',
                    node,
                });
            }
        });
    }

    // 插件被移除前做的事情
    beforePluginRemove() {
        this.unBindEvent();
    }

    // 插件被卸载前做的事情
    beforePluginDestroy() {
        this.unBindEvent();
    }
}

Demonstrate.instanceName = 'demonstrate';

export default Demonstrate;
